package com.terrynoya.fight.combat
{
	import com.terrynoya.fight.char.Character;
	import com.terrynoya.fight.char.Entity;
	import com.terrynoya.fight.emuns.AttackEffect;
	import com.terrynoya.fight.emuns.ContactType;
	import com.terrynoya.fight.emuns.Facing;
	import com.terrynoya.fight.emuns.PositionType;
	import com.terrynoya.fight.emuns.ProjectileDataType;
	import com.terrynoya.fight.emuns.StateTypeEnum;
	import com.terrynoya.fight.stateMachine.StateNumber;
	import com.terrynoya.fight.stateMachine.triggers.StateType;
	import com.terrynoya.geom.MVector2D;

	public class CombatChecker extends EngineObject
	{
		private var _attacks:Array;
		
		public function CombatChecker(engine:FightEngine)
		{
			super(engine);
		}
		
		public function run():void
		{
			this.runCharacterAttack();
			this.runProjectileAttacks();
		}
		
		private function runCharacterAttack():void
		{
			this.buildContacts();
			for (var i:int = 0; i < this._attacks.length; i++) 
			{
				var contact:Contact = this._attacks[i];
				if(contact.contactType == ContactType.Hit)
				{
					
				}
				this.runAttack(contact);
			}
		}
		
		/**
		 * 处理飞行道具攻击 
		 * 
		 */	
		private function runProjectileAttacks():void
		{
			var entities:EntityCollection = this.engine.entities;
			for (var i:int = 0; i < entities.length; i++) 
			{
				var projectile:ProjectTile = entities.getChildAt(i) as ProjectTile;
				if(projectile == null || projectile.canAttack == false)
				{
					continue;
				}
				this.checkProjectTile(projectile);
			}
		}
		
		private function checkProjectTile(projecTile:ProjectTile):void
		{
			var entities:EntityCollection = this.engine.entities;
			for (var i:int = 0; i < entities.length; i++) 
			{
				var entity:Entity = entities.getChildAt(i);
				var chara:Character = entity as Character;
				if(chara != null)
				{
					this.projectTileAttack(projecTile,chara);
				}
			}
		}
		
		private function projectTileAttack(projecTile:ProjectTile,chara:Character):void
		{
			var hitDef:HitDefinition = projecTile.data.hitDef;
			if(this.canHit(projecTile,chara,hitDef))
			{
				this.onAttackByProjecTile(projecTile,chara,hitDef,false);
			}
		}
		
		private function buildContacts():void
		{
			this._attacks = new Array();
			var len:int = this.engine.entities.length;
			for (var i:int = 0; i < len; i++) 
			{
				var entity:Entity = this.engine.entities.getChildAt(i);
				if(entity is Character)
				{
					this.hitCheck(Character(entity));
				}
			}
			
			this.checkHitCount();
		}
		
		/**
		 * 触发过的HitContact将不会多次触发 
		 * 
		 */		
		private function checkHitCount():void
		{
			var len:int = this.engine.entities.length;
			for (var i:int = 0; i < len; i++) 
			{
				var entity:Entity = this.engine.entities.getChildAt(i);
				var chara:Character = entity as Character;
				if(chara == null)
				{
					continue;
				}
				var hitCount:int = this.countHits(chara);
				if(hitCount > 0)
				{
					chara.offensiveInfo.hitCount += 1;
					chara.offensiveInfo.activeHitDef = false;
				}
				var blockCount:int = this.countBlocks(chara);
				if(blockCount > 0)
				{
					chara.offensiveInfo.activeHitDef = false;
				}
			}
		}
		
		private function countBlocks(attacker:Character):int
		{
			var count:int = 0;
			for (var i:int = 0; i < this._attacks.length; i++) 
			{
				var contact:Contact = this._attacks[i];
				if(contact.contactType == ContactType.Block && contact.attacker == attacker)
				{
					count ++;
				}
			}
			return count;
		}
		
		private function countHits(attacker:Character):int
		{
			var count:int = 0;
			for (var i:int = 0; i < this._attacks.length; i++) 
			{
				var contact:Contact = this._attacks[i];
				if(contact.contactType == ContactType.Hit && contact.attacker == attacker)
				{
					count++;
				}
			}
			return count;
		}
		
		private function hitCheck(attacker:Character):void
		{
			if(attacker.inHitPause || attacker.offensiveInfo.activeHitDef == false)
			{
				return;
			}
			for (var i:int = 0; i < this.engine.entities.length; i++) 
			{
				var entity:Entity = this.engine.entities.getChildAt(i);
				var target:Character = entity as Character;
				if(target == null)
				{
					continue;
				}
				var hitDef:HitDefinition = attacker.offensiveInfo.hitDef;
				if(this.canblock(attacker,target,hitDef,true) == true)
				{
					this._attacks.push(new Contact(attacker,target,hitDef,ContactType.Block));
				}
				else if(this.canHit(attacker,target,hitDef))
				{
					this._attacks.push(new Contact(attacker,target,hitDef,ContactType.Hit));
				}
				else if(this.canblock(attacker,target,hitDef,false))
				{
					this._attacks.push(new Contact(attacker,target,hitDef,ContactType.MissBlock));
				}
			}
		}
		
		private function runAttack(contact:Contact):void
		{
			if(contact.contactType == ContactType.Hit)
			{
				this.onAttack(contact.attacker,contact.target,contact.hitDef,false);
			}
			else if(contact.contactType == ContactType.Block)
			{
				this.onAttack(contact.attacker,contact.target,contact.hitDef,true);
			}
//			else if(contact.contactType == ContactType.MissBlock)
//			{
//				this.outOfRangeBlock(contact.target);		
//			}
		}
		
		private function outOfRangeBlock(target:Character):void
		{
			var stateNumber:int = target.stateManager.currentState.id;
			if(stateNumber < StateNumber.GuardStart || stateNumber > StateNumber.GuardEnd)
			{
				target.stateManager.changeState(StateNumber.GuardStart);
			}
		}
		
		private function onAttack(attacker:Character,target:Character,hitDef:HitDefinition,blocked:Boolean):void
		{
			target.defensiveInfo.onHit(hitDef,attacker,blocked);
			var myHitDef:HitDefinition = target.defensiveInfo.hitDef;
			attacker.offensiveInfo.onHit(myHitDef,target,blocked);
			
			if(blocked)
			{
				
			}
			else
			{
				this.makeSpark(attacker,target,myHitDef.sparkAnimation,myHitDef.sparkPosition,myHitDef.playerSpark);
			}
			
			if(blocked == false)
			{
				this.onAttackHit(attacker,target,hitDef);
			}
			else
			{
				trace("attack blocked");
				this.onAttackBlock(attacker,target,hitDef);
			}
		}
		
		private function onAttackByProjecTile(projecTile:ProjectTile,target:Character,hitDef:HitDefinition,blocked:Boolean):void
		{
			var attacker:Character = projecTile.creator;
			target.defensiveInfo.onHit(projecTile.data.hitDef,target,blocked);
			
			hitDef = target.defensiveInfo.hitDef;
			projecTile.totalHits ++;
			
			if(blocked)
			{
				
			}
			else
			{
				projecTile.hitPauseCountDown = hitDef.pauseTime;
				attacker.offensiveInfo.projecTileInfo.set(projecTile.data.projcTileId,ProjectileDataType.Hit);
				this.makeSparkByProjecTile(projecTile,target,hitDef.sparkAnimation,hitDef.sparkPosition,hitDef.playerSpark);
			}
			
			if(blocked == false)
			{
				trace("projectile hit!");
				this.onAttackHit(attacker,target,hitDef);
			}
			else
			{
				this.onAttackBlock(attacker,target,hitDef);
			}
		}
		
		private function onAttackBlock(attacker:Character,target:Character,hitDef:HitDefinition):void
		{
			target.defensiveInfo.hitTime = hitDef.guardHitTime;
			switch(target.defensiveInfo.hitStateType)
			{
				case StateTypeEnum.Standing:
				{
					target.stateManager.changeState(StateNumber.StandingGuardHitShaking);
					break;
				}
				case StateTypeEnum.Airborne:
				{
					target.stateManager.changeState(StateNumber.AirGuardHitShaking);
					break;
				}
				case StateTypeEnum.Crouching:
				{
					target.stateManager.changeState(StateNumber.CrouchingGuardHitShaking);
					break;
				}
			}
		}
		
		private function onAttackHit(attacker:Character,target:Character,hitDef:HitDefinition):void
		{
			var targetDef:DefensiveInfo = target.defensiveInfo;
			switch (targetDef.hitStateType)
			{
				case StateTypeEnum.Standing:
				{
					targetDef.hitTime = hitDef.groundHitTime;
					break;
				}
				case StateTypeEnum.Airborne:
				{
					break;
				}
			}
			
			if(hitDef.p1NewStateSetted)
			{
				attacker.stateManager.changeState(hitDef.p1NewState);
			}
			
			if(hitDef.p2NewStateSetted)
			{
				if(hitDef.p2UseP1State)
				{
					target.stateManager.foreignMachine = attacker.stateManager;
				}
				else
				{
					target.stateManager.foreignMachine = null;
				}
				target.stateManager.changeState(hitDef.p2NewState);
			}
			else
			{
				if(hitDef.groundAttackEffect == AttackEffect.Trip)
				{
					target.stateManager.changeState(StateNumber.HitTrip);
				}
				else
				{
					switch (target.defensiveInfo.hitStateType)
					{
						case StateTypeEnum.Standing:
						{
							target.stateManager.changeState(StateNumber.StandingHitShaking);
							break;
						}
						case StateTypeEnum.Airborne:
						{
							target.stateManager.changeState(StateNumber.AirHitShaking);
							break;
						}
						case StateTypeEnum.Crouching:
						{
							target.stateManager.changeState(StateNumber.CrouchingHitShaking);
							break;
						}
						case StateTypeEnum.Prone:
						{
							target.stateManager.changeState(StateNumber.HitProneShaking);
							break;
						}
					}
				}
			}
		}
		
		private function canHit(attacker:Entity,target:Character,hitDef:HitDefinition):Boolean
		{
			if(attacker == target)
			{
				return false;
			}
			var hitAttack:Boolean = attacker.collision.hitTestAttack(target.collision);
			return hitAttack;
		}
		
		private function canblock(attacker:Entity,target:Character,hitDef:HitDefinition,rangeCheck:Boolean):Boolean
		{
			if(attacker == target)
			{
				return false;
			}
			if(rangeCheck == true && attacker.collision.hitTestAttack(target.collision) == false)
			{
				return false;
			}
			if(target.commandManager.isActive("holdback") == false)
			{
				return false;
			}
			if(this.inGuardDistance(attacker,target,hitDef) == false)
			{
				return false;
			}
			return true;
		}
		
		private function inGuardDistance(attacker:Entity,target:Character,hitDef:HitDefinition):Boolean
		{
			var distance:Number = Math.abs(attacker.position.x - target.position.x);
			return distance <= hitDef.guardDistance;
		}
		
		private function makeSpark(attacker:Character,target:Character,animationId:int,sparkPos:MVector2D,playerAnimation:Boolean):void
		{
			var data:ExplodData = new ExplodData();
			data.isHitSpark = true;
			data.animationId = animationId;
			data.positionType = PositionType.P1;
			data.position = this.getSparkLocation(attacker,target,sparkPos);
			data.offsetor = target;
			data.creator = attacker;
			data.removeTime = -2;
			var explod:Explod = new Explod(this.engine,data);
			explod.engine.entities.add(explod);
		}
		
		private function makeSparkByProjecTile(projecTile:ProjectTile,target:Character,animationId:int,sparkPos:MVector2D,playerAnimation:Boolean):void
		{
			var data:ExplodData = new ExplodData();
			data.isHitSpark = true;
			data.animationId = animationId;
			data.positionType = PositionType.P1;
			data.position = sparkPos;
			data.offsetor = target;
			data.creator = projecTile.creator;
			data.removeTime = -2;
			var explod:Explod = new Explod(this.engine,data);
			explod.engine.entities.add(explod);
		}
		
		private function getSparkLocation(attacker:Character,target:Character,baseLocation:MVector2D):MVector2D
		{
			var offset:MVector2D = new MVector2D(0,attacker.position.y - target.position.y);
			switch(target.currentFacing)
			{
				case Facing.LEFT:
				{
					offset.x = target.position.x - target.getFrontLocation();
					break;
				}
				case Facing.RIGHT:
				{
					offset.x = target.getFrontLocation() - target.position.x;
					break;
				}
			}
			return baseLocation.add(offset);
		}
	}
}